package com.ly.utils;


import com.ly.model.Tuple;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

public class MyObjectUtil {

	private MyObjectUtil() {
	}

	public static <T> T initClass(Class<?> clazz, Object... args) throws InstantiationException {
		if (clazz.isInterface()) {
			throw new InstantiationException("Specified class is an interface");
		}
		try {
			Constructor<?> constructor = clazz.getDeclaredConstructor();
			if ((
						!Modifier.isPublic(constructor.getModifiers()) ||
						!Modifier.isPublic(constructor.getDeclaringClass()
													  .getModifiers())
				) && !constructor.isAccessible()) {
				constructor.setAccessible(true);
			}
			Object[] defaultArgs = new Object[args.length];
			for (int i = 0; i < args.length; i++) {
				defaultArgs[i] = args[i];
			}
			return (T) constructor.newInstance(defaultArgs);
		} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
		}
		return null;
	}

	public static <T> T getOrDefault(T t1, T t2) {
		return Objects.isNull(t1) ? t2 : t1;
	}

	public static Integer getId(Object obj) {
		return System.identityHashCode(obj);
	}

	public static boolean isPrimitive(Class<?> target) {
		return target.isPrimitive() || isClass(target, String.class) || isClass(target, Integer.class) ||
			   isClass(target, Double.class) || isClass(target, Boolean.class) || isClass(target, BigDecimal.class);
	}

	public static boolean isPrimitive(Object target) {
		return isPrimitive(target.getClass());
	}

	public static boolean isLocalDateTime(Class<?> target) {
		return isClass(target, LocalDateTime.class);
	}

	public static boolean isLocalDateTime(Object target) {
		return isLocalDateTime(target.getClass());
	}

	public static boolean isList(Class<?> target) {
		return isClass(target, Collection.class);
	}

	public static boolean isList(Object target) {
		return isClass(target, Collection.class);
	}

	public static boolean isMap(Class<?> target) {
		return isClass(target, Map.class);
	}

	public static boolean isMap(Object target) {
		return isMap(target.getClass());
	}

	public static boolean isEnum(Class<?> target) {
		return target.isEnum();
	}

	public static boolean isEnum(Object target) {
		return isEnum(target.getClass());
	}

	public static boolean isClass(Object object, Class<?> clazz) {
		return isClass(object.getClass(), clazz);
	}

	public static boolean isClass(Class<?> target, Class<?> clazz) {
		return clazz.isAssignableFrom(target);
	}

	public static <T> void update(T t, T set) {
		Class<?> aClass = t.getClass();
		while (!aClass.equals(Object.class)) {
			Field[] declaredFields = aClass.getDeclaredFields();
			Arrays.stream(declaredFields)
				  .filter(field -> !Modifier.isStatic(field.getModifiers()))
				  .filter(field -> !Modifier.isFinal(field.getModifiers()))
				  .filter(field -> !Modifier.isTransient(field.getModifiers()))
				  .forEach(field -> {
					  field.setAccessible(true);
					  try {
						  Object value = field.get(set);
						  if (Objects.nonNull(value)) {
							  field.set(t, value);
						  }
					  } catch (IllegalAccessException e) {
						  e.printStackTrace();
					  }
				  });
			aClass = aClass.getSuperclass();
		}
	}

	public static Map<String, Object> getMap(Object object) {
		Map<String, Object> rtn = new HashMap<>();
		Class<?> aClass = object.getClass();
		while (!aClass.equals(Object.class)) {
			Field[] declaredFields = aClass.getDeclaredFields();
			Map<String, Object> map = Arrays.stream(declaredFields)
											.filter(field -> !Modifier.isStatic(field.getModifiers()))
											.filter(field -> !Modifier.isFinal(field.getModifiers()))
											.filter(field -> !Modifier.isTransient(field.getModifiers()))
											.map(field -> {
												field.setAccessible(true);
												try {
													Object value = field.get(object);
													if (Objects.nonNull(value)) {
														return new Tuple<>(field.getName(), value);
													}
												} catch (IllegalAccessException e) {
													e.printStackTrace();
												}
												return null;
											})
											.filter(Objects::nonNull)
											.collect(Collectors.toMap(Tuple::getLeft, Tuple::getRight, (a, b) -> a));
			rtn.putAll(map);
			aClass = aClass.getSuperclass();
		}
		return rtn;
	}
}